package plp.funcoo.expressions;

import plp.funcoo.environment.compilation.CompilationEnvironment;
import plp.funcoo.environment.compilation.Environment;
import plp.funcoo.environment.execution.ExecutionContext;
import plp.funcoo.environment.execution.ExecutionEnvironment;
import plp.funcoo.exceptions.ClassNotDeclared;
import plp.funcoo.exceptions.MethodAlreadDeclared;
import plp.funcoo.exceptions.MethodNotDeclared;
import plp.funcoo.exceptions.VarAlreadyDeclared;
import plp.funcoo.exceptions.VarNotDeclared;
import plp.funcoo.types.PrimitiveType;
import plp.funcoo.util.Type;
import plp.funcoo.value.IntegerValue;
import plp.funcoo.value.StringValue;


/**
* Um objeto desta classe representa uma Expressao de Concatenacao entre
* objetos <code>ValorString</code>
*/
public class ExpConcat extends BinaryExpression{
	/**
	 * Constroi uma Expressao de Concatenacao com as sub-expressoes especificadas.
	 * Estas sub-expressoes devem ser tais que a avaliacao das mesmas resulta
	 * em <code>ValorString</code>
	 *
	 * @param esq Expressao da esquerda
	 * @param dir Expressao da direita
	 */
	
	public ExpConcat(Expression left, Expression right){
		super(left, right, "++");
	}
	/**
	 * Retorna o valor da Expressao de Concatenacao
	 */
	@Override
	public ExecutionEnvironment evaluate(ExecutionEnvironment envi) throws VarNotDeclared, MethodNotDeclared, VarAlreadyDeclared, MethodAlreadDeclared {
		
		envi = getLeft().evaluate(envi); 
		StringValue l = (StringValue) ((ExecutionContext) envi).getValueAnalyzed();
		envi = getRight().evaluate(envi); 
		StringValue r = (StringValue) ((ExecutionContext) envi).getValueAnalyzed();
		((ExecutionContext) envi).setValueAnalyzed(new StringValue( l.getValue() + r.getValue() ));
		return envi;
		
	}
	
	/**
	 * Retorna os tipos possiveis desta expressao.
	 *
	 * @return os tipos possiveis desta expressao.
	 */
	@Override
	public Type getType(Environment envi) throws VarNotDeclared,
			ClassNotDeclared {
		return PrimitiveType.Type_STRING;
	}
	/**
	 * Realiza a verificacao de tipos desta expressao.
	 *
	 * @return <code>true</code> se os tipos da expressao sao validos;
	 *          <code>false</code> caso contrario.
	 */
	@Override
	protected boolean checkTypeTerminal(CompilationEnvironment envi)
			throws VarNotDeclared, ClassNotDeclared {
		return getLeft().getType(envi).isString() && getRight().getType(envi).isString();
	}
	@Override
	public Expression makeCopy() {
		return new ExpConcat(this.getLeft().makeCopy(), this.getRight().makeCopy());
	}

}
